﻿open System


(* Simple Agent *)
let mailbox = 
    MailboxProcessor.Start(fun mb ->
        let rec loop x =
            async { let! msg = mb.Receive()
                    let x = x + msg
                    printfn "Running total: %i - new value %i" x msg
                    return! loop x }
        loop 0)
        
mailbox.Post(1)
mailbox.Post(2)
mailbox.Post(3)



(* Prototype for Forecast Infrastructure *)

(* Recursive Type to house a single computation or a list of computations *)
type job = 
| Computation of (int -> int) * int * string
| Computations of job list
(* Result message to be sent to result collector *)
type jobResult = (string * int) 

(* Result Collecting Agent *)
let ResultCollector = 
        MailboxProcessor.Start(fun msg ->
            let rec loop x = 
                async {
                        let! (result: jobResult) = msg.Receive()
                        let w,r = result
                        printf "Result received from worker: %s result: %i \n" w r
                        return! loop ( x + 1)
                       }
            loop 0)



(* Helper For Kicking Off an Agent to do some work -> Post result to a barrier *)
let kickoff f initval wrkerIdentity completed_channel = 
    let worker = MailboxProcessor.Start(fun msg ->
                 let rec loop x =  
                    async {
                             let! func,initialstate,identity,(channel: MailboxProcessor<jobResult>) = msg.Receive() 
                             let result = func initialstate
                             channel.Post((identity,result))
                            }
                 loop 0)

    worker.Post(f,initval,wrkerIdentity,completed_channel)

  

let supervisor = 
    MailboxProcessor.Start(fun msg ->
        let barrier = ResultCollector 
        let rec loop x = 
            async {
                    let! (incoming: job) = msg.Receive()
                    match incoming with
                    | Computation(f,initialval,identity) ->
                             barrier.Post((identity, (f initialval)))
                             return! loop(x + 1) //keep this thing going!
                    | Computations jobs ->
                            List.iter(fun j ->
                                        match j with
                                        | Computation(f,initval,identity) ->
                                            kickoff f initval identity barrier
                                        | _  -> failwith "Nested Jobs Not Supported" ) jobs 
                            return! loop(x + 1) //keep this thing going!
                    }
        loop 0)



let id = fun _ -> Guid.NewGuid().ToString()
let fanout = [Computation((fun x -> x * x), 5, id());
              Computation((fun x -> x * x * x), 5, id());
              Computation((fun x -> x * x * x * x), 5, id());
              Computation((fun x -> x * x * x * x * x), 5, id())]

                
let fanout2 = 
            [ Computation((fun y -> y * (20 / 2)), 10, id());
              Computation((fun z -> z * (1000 * 5 + 4 - 20)), 5, id()) ]






supervisor.Post(Computations(fanout))
supervisor.Post(Computations(fanout2))